////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2012 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h>
#include "..\FM3.h"
#include "FM3_Flash.h"

#define LCD_DEBUG_FLASH_EraseBlock
//#define LCD_DEBUG_FLASH_Write

#define FLASH_DPOLL 0x80        // data pollbit
#define FLASH_DQ6 0x00000040    // Data toggle flag bit (TOGG) position
#define FLASH_DQ5 0x00000020    // Time limit exceeding flag bit (TLOV) position

UINT32 sector_top(UINT32 addr)
{
    if (addr < 0x00004000)
        return 0x0000;
    else if (addr < 0x00008000)
        return 0x4000;
    else if (addr < 0x00020000)
        return 0x8000;
    else if (addr < 0x00040000)
        return 0x20000;
    else if (addr < 0x00060000)
        return 0x40000;
    else if (addr < 0x00080000)
        return 0x60000;
#ifdef MB9BF618T
    else if (addr < 0x000A0000)
        return 0x80000;
    else if (addr < 0x000C0000)
        return 0xA0000;
    else if (addr < 0x000E0000)
        return 0xC0000;
    else if (addr < 0x00100000)
        return 0xE0000;
    else {
        lcd_printf("sector_top err\r\n");
        return 0xE0000;
    }
#endif
#ifdef MB9BF506N
    else {
        lcd_printf("sector_top err\r\n");
        return 0x60000;
    }
#endif
}
BOOL FM3_Flash_Driver::ChipInitialize(void *context)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FLASH)
    debug_printf("ChipInitialize()\r\n");
#endif
    return TRUE;
}

BOOL FM3_Flash_Driver::ChipUnInitialize(void *context)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FLASH)
    debug_printf("ChipUnInitialize()\r\n");
#endif
    return TRUE;
}

const BlockDeviceInfo* FM3_Flash_Driver::GetDeviceInfo(void *context)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FLASH)
    debug_printf("GetDeviceInfo(context=%x))\r\n", context);
#endif
    BLOCK_CONFIG *config = (BLOCK_CONFIG *)context;
    return config->BlockDeviceInformation;
}

BOOL FM3_Flash_Driver::Read(void *context, ByteAddress Address, UINT32 NumBytes, BYTE *pSectorBuff)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FLASH) || defined(DEBUG_FLASH_Read)
    debug_printf("Read(addr=%x, num=%x, psec=%x)\r\n", Address, NumBytes, pSectorBuff);
#endif
    CHIP_WORD *startAddress = (CHIP_WORD *)Address;
    CHIP_WORD *endAddress = (CHIP_WORD *)(Address + NumBytes);
    while (startAddress < endAddress) {
        *pSectorBuff++ = *startAddress++;
    }
    return TRUE;
}

BOOL IsErased(ByteAddress Address, UINT32 NumBytes)
{
    BOOL ret = TRUE;
    while (NumBytes-- > 0) {
        if (*((UINT8 *)Address) != 0xFF) {
            ret = FALSE;
            break;
        }
        Address++;
    }
    return ret;
}

BOOL FM3_Flash_Driver::Write(void *context, ByteAddress Address, UINT32 NumBytes, BYTE *pSectorBuff, BOOL ReadModifyWrite)
{
#if defined(DEBUG_FLASH) || defined(DEBUG_FLASH_Write)
    debug_printf("Write(addr=%x, num=%x, psec=%x)\r\n", Address, NumBytes, pSectorBuff);
#endif
#ifdef LCD_DEBUG_FLASH_Write
    lcd_printf("Write(%x) %x", Address, NumBytes);
#endif
    BOOL ret = TRUE;
    if (!IsErased(Address, NumBytes)) {
#ifdef LCD_DEBUG_FLASH_Write
        lcd_printf(" - NG: Not Erased\r\n");
#endif
        return FALSE;
    }
    FM3_FLASH_IF_TypeDef *flash_if = FM3_FLASH_IF;
    volatile UINT32 dummy;
    volatile UINT16 dummy1, src_dpoll;
    volatile UINT32 timeout;
    volatile UINT16 *startAddress = (UINT16 *)((UINT32)Address);
    volatile UINT16 *endAddress = (UINT16 *)((UINT32)Address + NumBytes);
    volatile UINT16 *src = (UINT16 *)pSectorBuff;
    GLOBAL_LOCK(FlashIrq);
    flash_if->FASZR = (UINT32)0x0001;
    dummy = flash_if->FASZR;
    while (startAddress < endAddress) {
        *(volatile UINT16 *)0x1550 = (UINT16)0x00AA;
        *(volatile UINT16 *)0x0AA8 = (UINT16)0x0055;
        *(volatile UINT16 *)0x1550 = (UINT16)0x00A0;
        *startAddress     = *src;
        dummy1 = *startAddress;    // dummy read
        src_dpoll = *src & FLASH_DPOLL;
        timeout = 10000;
        while (TRUE) {
            if ((*startAddress & FLASH_DPOLL) == src_dpoll)
                break;
            if (timeout-- == 0)
                break;
        }
        if ((timeout == 0) && ((*startAddress & FLASH_DPOLL) != src_dpoll)) {
            ret = FALSE;
            break;
        }
        startAddress++;
        src++;
    }
    flash_if->FASZR = (UINT32)0x0002;
    dummy = flash_if->FASZR;
    if (memcmp((void *)pSectorBuff, (void *)Address, NumBytes) != 0)
        ret = FALSE;
#ifdef LCD_DEBUG_FLASH_Write
    lcd_printf(":%d\r\n", ret);
#endif
    return ret;
}

BOOL FM3_Flash_Driver::Memset(void *context, ByteAddress Address, UINT8 Data, UINT32 NumBytes)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FLASH) || defined(DEBUG_FLASH_Memset)
    debug_printf("Memset(addr=%x, num=%x, data=%x)\r\n", Address, NumBytes, Data);
#endif
    return FALSE;
}

BOOL FM3_Flash_Driver::GetSectorMetadata(void *context, ByteAddress SectorStart, SectorMetadata *pSectorMetadata)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FLASH)
    debug_printf("GetSectorMetadata(start=%x, psecmetadata=%x)\r\n", SectorStart, pSectorMetadata);
#endif
    return FALSE;
}

BOOL FM3_Flash_Driver::SetSectorMetadata(void *context, ByteAddress SectorStart, SectorMetadata *pSectorMetadata)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FLASH)
    debug_printf("SetSectorMetadata(start=%x, psecmetadata=%x)\r\n", SectorStart, pSectorMetadata);
#endif
    return FALSE;
}

BOOL FM3_Flash_Driver::IsBlockErased(void *context, ByteAddress Address, UINT32 BlockLength)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FLASH)
    debug_printf("IsBlockErased(addr=%x, len=%x)\r\n", Address, BlockLength);
#endif
    CHIP_WORD *startAddress = (CHIP_WORD *)Address;
    CHIP_WORD *endAddress = (CHIP_WORD *)(Address + BlockLength);
    while (startAddress < endAddress)
        if (*startAddress++ != (CHIP_WORD)0xFFFFFFFF)
            return FALSE;
    return TRUE;
}

BOOL FM3_Flash_Driver::EraseBlock(void *context, ByteAddress Address)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FLASH) || defined(DEBUG_FLASH_EraseBlock)
    debug_printf("EraseBlock(addr=%x)\r\n", Address);
#endif
#ifdef LCD_DEBUG_FLASH_EraseBlock
    lcd_printf("EraseBlock(%x)\r\n", Address);
#endif
    GLOBAL_LOCK(FlashIrq);
    FM3_FLASH_IF_TypeDef *flash_if = FM3_FLASH_IF;
    volatile UINT32 dummy;
    flash_if->FASZR = (UINT32)0x0001;
    dummy = flash_if->FASZR;
    UINT32 off = sector_top((UINT32)Address);
    UINT32 i;
    BOOL ret = TRUE;
    for (i = 0; i < 2 ; i++) {
        if (i)
            off += 4;
        *(volatile UINT16 *)0x1550 = (UINT16)0x00AA;
        *(volatile UINT16 *)0x0AA8 = (UINT16)0x0055;
        *(volatile UINT16 *)0x1550 = (UINT16)0x0080;
        *(volatile UINT16 *)0x1550 = (UINT16)0x00AA;
        *(volatile UINT16 *)0x0AA8 = (UINT16)0x0055;
        *(volatile UINT16 *)off    = (UINT16)0x0030;
        volatile UINT16 dummy1;
        volatile UINT16 dummy2;
        dummy1 = *(UINT16 *)off;
        while (TRUE) {
            dummy1 = *(UINT16 *)off;
            dummy2 = *(UINT16 *)off;
            if ((dummy1 & FLASH_DQ6) == (dummy2 & FLASH_DQ6))
                break;
            if (dummy1 & FLASH_DQ5) {
                break;
            }
        }
        dummy1 = *(UINT16 *)off;
        dummy2 = *(UINT16 *)off;
        if ((dummy1 & FLASH_DQ6) != (dummy2 & FLASH_DQ6)) {
            ret = FALSE;
            break;
        }
    }
    flash_if->FASZR = (UINT32)0x0002;
    dummy = flash_if->FASZR;
    return ret;
}

void FM3_Flash_Driver::SetPowerState(void *context, UINT32 State)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FLASH)
    debug_printf("SetPowerState(state=%x)\r\n", State);
#endif
    return;
}

UINT32 FM3_Flash_Driver::MaxSectorWrite_uSec(void *context)
{
#if defined(DEBUG_FLASH)
    debug_printf("MaxSectorWrite_uSec()\r\n");
#endif
    NATIVE_PROFILE_PAL_FLASH();
    MEMORY_MAPPED_NOR_BLOCK_CONFIG *config = (MEMORY_MAPPED_NOR_BLOCK_CONFIG *)context;
    return config->BlockConfig.BlockDeviceInformation->MaxSectorWrite_uSec;
}

UINT32 FM3_Flash_Driver::MaxBlockErase_uSec(void *context)
{
#if defined(DEBUG_FLASH)
    debug_printf("MaxBlockErase_uSec()\r\n");
#endif
    NATIVE_PROFILE_PAL_FLASH();
    MEMORY_MAPPED_NOR_BLOCK_CONFIG *config = (MEMORY_MAPPED_NOR_BLOCK_CONFIG *)context;
    return config->BlockConfig.BlockDeviceInformation->MaxBlockErase_uSec;
}

struct IBlockStorageDevice g_FM3_Flash_DeviceTable =
{
    &FM3_Flash_Driver::ChipInitialize,
    &FM3_Flash_Driver::ChipUnInitialize,
    &FM3_Flash_Driver::GetDeviceInfo,
    &FM3_Flash_Driver::Read,
    &FM3_Flash_Driver::Write,
    &FM3_Flash_Driver::Memset,
    &FM3_Flash_Driver::GetSectorMetadata,
    &FM3_Flash_Driver::SetSectorMetadata,
    &FM3_Flash_Driver::IsBlockErased,
    &FM3_Flash_Driver::EraseBlock,
    &FM3_Flash_Driver::SetPowerState,
    &FM3_Flash_Driver::MaxSectorWrite_uSec,
    &FM3_Flash_Driver::MaxBlockErase_uSec,
};
